/**
 * 
 */
package edu.rice.myxo.sim;

import java.io.*;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import edu.rice.myxo.utils.*;

/**
 * @author Haiyang Zhang This class contains external parameters All parameters
 *         are public and can be accessed from any class
 * 
 */
public class Global {

	/**
	 * This method reads in parameters from a parameter file and assign the
	 * value according to parameter name. The parameter file should have the
	 * format of: parameter_name=parameter_value
	 * 
	 * @param filename
	 * @throws IOException
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 * @throws IllegalArgumentException
	 * @throws IllegalAccessException
	 */
	public static void getParam(String filename) throws IOException,
			SecurityException, IllegalArgumentException,
			IllegalAccessException {
		File file = new File(filename);
		if (file.exists()) {
			Scanner sc = new Scanner(file);
			while (sc.hasNext()) {
				String paraline = sc.nextLine();
				paraline.trim();
				if(paraline.equals(""))
					continue;
				if (paraline.contains("#")) {
					continue;
				}
				Scanner line = new Scanner(paraline).useDelimiter("=");
				String name = line.next();
				try{
				if (line.hasNextBoolean()) {
					Global.class.getField(name).setBoolean(null,
							line.nextBoolean());
				} else if (line.hasNextInt()) {
					Global.class.getField(name).setInt(null, line.nextInt());
				} else if (line.hasNextDouble()) {
					Global.class.getField(name).setDouble(null,
							line.nextDouble());
				}
				}catch(NoSuchFieldException e){
					continue;
				}
			}
			sc.close();
			if (reversingTime != 0) {
				vPhase = 2 * Math.PI / reversingTime;
			}
			sigToNoSig = noSigRatio/1000;
			noSigToSig = (1 - noSigRatio) * sigToNoSig / noSigRatio;
			coneAngle = Math.toRadians(coneAngle);
		} else {
			System.out.println("Configuration file does noe exist!");
		}
	}

	/**
	 * This method write all global parameters to a file specified by the
	 * filename. The file has the format of: parameter_name=parameter_value
	 * 
	 * @param dirname
	 * @throws IOException
	 * @throws IllegalArgumentException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 */
	public static void writeParam(String dirname) throws IOException,
			IllegalArgumentException, SecurityException, IllegalAccessException {
		String filename = dirname + "//" + "config.txt";
		FileWriter fw = new FileWriter(filename);
		PrintWriter pw = new PrintWriter(fw);
		for (int i = 0; i < Global.class.getFields().length; i++) {
			pw.print(Global.class.getFields()[i].getName());
			pw.print("=");
			pw.print(Global.class.getFields()[i].get(null));
			pw.println();
		}
		pw.close();
		fw.close();
	}

	/**
	 * Read in phase resetting map file specified by the String resettingmap.
	 * The phase resetting map file is a text file contains only values. The
	 * value is phase/2PI. So when we use phase resetting map, we use equation:
	 * new_phase = old_phase + phase_resetting * 2 * PI.
	 * 
	 * @param resettingmap
	 * @throws IOException
	 */
	public static void getResettingMap(String resettingmap) throws IOException {
		File file = new File(resettingmap);
		if (file.exists()) {
			Scanner sc = new Scanner(file);
			ArrayList<Double> rm = new ArrayList<Double>();
			while (sc.hasNext()) {
				rm.add(sc.nextDouble());
			}
			resettingMap = new double[rm.size()];
			for (int i = 0; i < rm.size(); i++) {
				resettingMap[i] = rm.get(i);
			}
		} else {
			System.out.println("Resetting map does not exist!");
		}
	}

	/**
	 * Manually change the shape of the resetting map. REFINE_TYPE == 1: longer
	 * refractory period
	 * 
	 * This method only works with current phase resetting map.
	 * 
	 * @param rmap
	 * @param REFINE_TYPE
	 */
	public static void refineResettingMap(double[] rmap, int REFINE_TYPE) {
		double[] newrmap = new double[rmap.length];
		switch (REFINE_TYPE) {
		case 1:
			System.out.println("Increase refractory period");
			for (int i = 0, j = 0; i < 73; i++, j += 2) {
				newrmap[j] = rmap[i];
				newrmap[j + 1] = rmap[i];

			}
			for (int i = 146; i < newrmap.length; i++) {
				int mapindex = (520 - 73) * (i - 146) / (520 - 146) + 73;
				newrmap[i] = rmap[mapindex];
			}
			break;
		default:
			System.out.println("Did not refine!");
			newrmap = rmap;
			break;
		}
		Global.resettingMap = newrmap;
	}


	// public static double colAgentNum;
	// simType = 1, Speed is independent (rippling)
	// simType = 2, Speed is dependent on local cell density
	public static double simType;

	public static double densityRange;

	public static double initialType;

	public static double densityThreshold;

	public static double velocityChange;

	public static double vChangePro;

	public static double turn;

	public static double frictionCoefficient;

	public static double saveTimePoint;

	public static double phaseDiffCoef;

	public static double velocityDiffCoef;

	public static double positiveSignalFraction;

	public static double alignPro;

	public static double threadN;

	// public static double collidingCell;

	public static double averageQualifiedSignalCellNum;

	public static double meanDir;

	public static double refractoryPeriod;

	public static double phaseJump;

	public static double phaseJumpProbability;

	public static int collisionType;

	public static int collisionResolveType;

	public static double cellLength;

	public static double cellWidth;

	public static double vAmoter;

	public static double vInSlime;

	public static double vSmoter;

	public static double vPhase;

	public static double reversingTime;

	public static double vPhaseInSlime;

	public static double vReproduct;

	public static double worldX;

	public static double worldY;

	public static double originX;

	public static double originY;

	public static double coneAngle;

	public static double cSiganlStartX;

	public static double cSignalStartY;

	public static double cSiganlWidth;

	public static double cSignalHeight;

	public static double cSignalType;

	public static double dT;

	public static double gridSize;

	public static double pixSize;

	public static int cellNum;

	public static int labeledCellNum;

	public static int refineMapType;

	public static int timeIndex;

	public static int fluxType;

	public static double fluxBoundary;

	public static double boundX;

	public static double boundY;

	public static double boundWidth;

	public static double boundHeight;

	public static double boundCenterX;

	public static double boundCenterY;

	public static double boundR;

	// This method means plot cell every outputNumRatio cells
	public static int outputNumRatio;

	public static double trackStartTime;

	public static double trackEndTime;

	public static double outputDt;

	public static double simTime;

	public static double iniRadius;

	public static double iniWidth;

	public static double iniHeight;

	public static double ringWidth;

	public static double maxNeighborDis;

	public static double maxSlimeDis;

	public static double slimeLife;

	public static double noSigRatio;

	public static double sigToNoSig;

	public static double noSigToSig;

	// public static double extend;

	public static double ACP;

	public static double ASP;

	public static double AAP;

	public static double DCP;

	public static double DSP;

	public static double DAP;

	public static double JSP;

	public static double BSP;

	public static double spaceDiffCoef;

	public static double xDiffCoef;

	public static double yDiffCoef;

	public static double dirDiffCoef;

	public static double vPhaseDiffCoef;

	public static double windShield;

	public static double edgeCellFraction;

	public static double currTime;

	public static double stuckPro;

	public static boolean SMOTOR;

	public static boolean AMOTOR;

	public static boolean IFALIGNED;

	public static boolean SIGNAL;

	public static boolean PERBOUNDX;

	public static boolean PERBOUNDY;

	public static boolean DEBUG;

	public static double[] resettingMap;

	public static double[] distanceExpan;

	public static int[] cellFluxArray;

	public static int[] cellNumberArray;

	public static double[] meanSquareDisplace;

	public static double[] msdEarly;

	public static double[] msdMiddle;

	public static double[] msdLate;

	public static double[] signalTrack;

	public static double[] vArray;
    
    public static ArrayList<double[]>[] reversalpoints;

	public static String dirName;

	public static String imageDirName;

	public static Random r = new Random();

//	public static ArrayList<Double> reverPeriods = new ArrayList<Double>();
	
	public static Vector<Double> reverPeriods = new Vector<Double>();

//	public static ArrayList<Double> distancePerReverse = new ArrayList<Double>();
	
	public static Vector<Double> distancePerReverse = new Vector<Double>();

//	public static ArrayList<Double> refractorTime = new ArrayList<Double>();
	
	public static Vector<Double> refractorTime = new Vector<Double>();

	public static ArrayList<MyxoAgent> mirrorAgents;

	public static int numCollisions;

	public static double localDensity;
	
	public static double cellpaircount=0;
	
	public synchronized static void inccpc(double v){
		cellpaircount += v;
	}

	/*
	 * These are temporary arrays used by multithreading
	 */
	
	public static double[] cpcparts;
	public static double[] ovs;
	public static double[] gos;
	public static double[] ocs;
	
	///////////////////////////////////////////
	
	public static double[][] allAgents;

	// /////////////////// Params in Olexii model
	public static double sigRange;

	public static double lowV;

	public static double alignToNeighbor;

	public static double alignToAggBound;

	public static double streamSigCoef;

	public static double collisionSigCoef;

	public static double gradDen0;

	public static double speedCorrelation;

}
